function mitt() {
  let all = new Map();
  /**
   * 订阅方法
   * @param {*} key  传递的事件key
   * @param {*} handler  事件处理函数
   */
  const on = (key, handler) => {
    // 如果没有key 对应的数组，则创建一个
    if (!all.get(key)) {
      all.set(key, []);
    }
    // 将cb 存入对应的key 对应的数组中
    all.get(key).push(handler);
    
  };
  /**
   * 发布方法
   * @param {*} key 发布给订阅者的key
   * @param {*} params  发布的参数，告诉订阅者我发布了什么内容。
   */
  const emit = (key, params) => {
    // 判断是否有对应的key
    let handlers = all.get(key);
    // if (!handlers) return; // 这不能return 啊，如果是* 后面还要做判断滴
    // 如果存在对应的key
    if (handlers) {
      // 获取对应的key 对应的数组
      // 遍历数组，执行对应的事件处理函数
      handlers.slice().map((handler) => handler(params));
    }
    // 如果没有查到监听事件，看是否监听了所有事件
    handlers = all.get('*');
    // 如果是订阅了所有事件，则以此执行订阅的* 事件
    if (handlers) {
        handlers.slice().map((handler) => handler(params));
    }
  };
  /** 移除事件方法
   * @param {*} key 
   * @param {*} handler?  如果传入handler，则移除制定的handler。如果没有传入handler，则移除key下所有的handler
   */
  const off = (key, handler) => {
    let handlers = all.get(key);

    console.log('%c [  ]-46', 'font-size:13px; background:pink; color:#bf2c9f;', handlers)
    if (handlers) {
        // handler ? handlers.splice(handlers.indexOf(handler), 1) : handlers.length =0;
        handler ? handlers.splice(handlers.indexOf(handler), 1) : all.set(key, []);
    }
    console.log('all',all)
  };
  /**
   * 只执行一次方法：内部也是调用on，执行完，取消掉
   * @param {*} type 
   * @param {*} handler 
   */
  const once = (type, handler) => {
    const onceHandler = (params)=>{
        handler(params);
        off(type, handler);
    }
    on(type,onceHandler)
  };
  return {
    all,
    on,
    off,
    emit,
    once,
  };
}
const emitter = mitt();
emitter.on("foo", (params) => {
  console.log("foo1!",params);
});
emitter.once("foo", (params) => {
  console.log("foo2!",params);
});
// emitter.once("foo", (params) => {
//   console.log("foo!",params);
// });
emitter.on("*", (params) => {
    console.log("*!",params);
})
// emitter.off('foo')
emitter.emit("foo", "w");

// emitter.all.clear()
console.log("emitter", emitter);
//  问题思考
//  - 整体流程是什么？
// 先订阅，看有没有对应的key,没有就创建一个消息队列（前端就是创建一个数组），来存放订阅的事件。等到发布者根据key，发布事件，会传递参数给订阅者，订阅者此时会收到发布者发来的消息。
//  - 怎么取消订阅？
// 先判断有没有对应的key， 并且 有要移除的函数。，则从对应的key 对应的数组中，移除对应的订阅的事件。如果没有要移除的函数，则降对应key 下所有的事件队列清空
//  - 怎么只执行一次？
// 先订阅，订阅的时候，创建一个新的函数，这个函数会执行一次，然后取消订阅。

//  - 怎么发布所有事件？
// 先判断有没有订阅所有事件，如果有，则遍历所有事件，执行对应的事件处理函数。
// 事件订阅优缺点？
// 优点：
// 简化通信： 不用考虑组件层级，组件间可以直接通信，不用一个向上/下 传递 属性/事件。是一个全局性质的模式

// 缺点：
// 容易滥用： 其实组件之间本身是需要关系的，大量使用事件总线使得组件之间的关系变得模糊
// 难以跟踪：事件总线模式可能会导致事件流变得难以跟踪和理解，特别是在大型应用程序中。由于事件的传递是异步的，因此可能难以确定事件的来源和处理过程。

